/* Ty, a programming language interpreter
 * Copyright (C) 2007 Nick Thomas
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include "image.h"

void ty_make_image(ty_image *image)
{
    ty_make_stack(&image->data_stack);
    ty_make_heap(&image->heap);
}

void ty_make_heap(ty_heap *heap, uintptr_t size)
{
    heap->size = TY_HEAP_INITIAL_SIZE;
    heap->end = 0;
    heap->heap = ty_malloc(sizeof(ty_chunk) * heap->size);
    heap->other_heap = ty_malloc(sizeof(ty_chunk) * heap->size);
    heap->mark_array = ty_malloc(heap->size);
}

ty_chunk *ty_new_chunk(ty_image *image)
{
    if (heap->end == heap->size) {
        ty_do_garbage_collection(image);
    }

    return &heap->heap[heap->end++];
}

/* XXX: I wrote this code when I was tired, so review it to sniff out
 * any bugs.
 */

/* A helper function for the garbage collector. */
void ty_copy_chunk(ty_heap *heap, ty_obj *obj)
{
    ty_chunk *chunk = ty_obj_chunk(*obj);
    uintptr_t index = (uintptr_t)(chunk - heap->heap);

    /* If the chunk has not been copied, copy it. */
    if (!heap->mark_array[index]) {
        memcpy(&heap->other_heap[heap->end], &heap->heap[index],
                sizeof(ty_chunk));
        heap->heap[index].forward_ptr = &heap->other_heap[heap->end];
        heap->end++;
    }

    /* Adjust the ty_obj to point to the object's new location. */
    *obj = ty_make_obj(heap->heap[index].forward_ptr, ty_obj_type(*obj));
}

void ty_do_garbage_collection(ty_image *image)
{
    ty_heap *heap = &image->heap;
    ty_data_stack *stack = &image->data_stack;

    heap->end = 0;
    memset(heap->mark_array, 0, heap->size);

    /* Copy all chunks referenced by the data stack. */
    for (int i = 0; i < stack->top; i++) {
        ty_copy_chunk(heap, &stack->data[i]);
    }
}
